package org.apache.lucene.index.tag;

import java.io.IOException;

import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.RAMOutputStream;

public class TagIndex {
  Directory directory;
  TermIndexReader termIndexReader;

  public class TermIndexWriter {
    IndexOutput output;
    int lastIndex = 0;

    void write(TagTerm term, long position, int index) throws IOException {
      output.writeVInt(term.fieldNumber);
      output.writeVInt(index - lastIndex);
      output.writeString(term.text);
      output.writeVLong(position);
      lastIndex = index;
    }
  }

  public static class TagTerm implements Comparable<TagTerm> {
    public int fieldNumber;
    public String text;
    public int index;

    public TagTerm() {
    }

    public TagTerm(int fieldNumber, String text, int index) {
      this.fieldNumber = fieldNumber;
      this.text = text;
      this.index = index;
    }

    public final int compareTo(TagTerm other) {
      if (fieldNumber == other.fieldNumber)
        return text.compareTo(other.text);
      else
        return compareInt(fieldNumber, other.fieldNumber);
    }
  }

  public final static int compareInt(int thisVal, int anotherVal) {
    return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
  }

  public class TermIndexReader {
    IndexInput input;
    TagTerm[] terms;
    long[] pointers;
    int size;

    public TermIndexReader() {

    }

    public void loadTerms() throws IOException {
      size = input.readInt();
      pointers = new long[size];
      int lastIndex = 0;
      for (int x=0; x < size; x++) {
        int fieldNumber = input.readVInt();
        int index = lastIndex + input.readVInt();
        String text = input.readString();
        pointers[x] = input.readVLong();
        terms[x] = new TagTerm(fieldNumber, text, index);
      }
    }
  }

  public final static class TagTermInfo {
    int docsLength;
    long docsPointer;
    long postingsPointer;
    int docFreq;
    int skipOffset;
  }
  
  // TODO: implement previous by recording term pointers since last indexed term
  public class TermsReader {
    TagTerm[] indexTerms;
    long[] indexPointers;
    TagTermBuffer scanBuffer;
    TagTermBuffer termBuffer;
    TagTermBuffer prevBuffer;
    int index = 0;
    int size;
    IndexInput input;
    TagTermInfo tagTermInfo = new TagTermInfo();
    TagTerm tagTerm = new TagTerm();
    
    public TermsReader() {
      indexTerms = termIndexReader.terms;
      indexPointers = termIndexReader.pointers;
    }
    
    private final int getIndexOffset(TagTerm tagTerm) {
      int lo = 0;
      int hi = indexTerms.length - 1;
      while (hi >= lo) {
        int mid = (lo + hi) >> 1;
        int delta = tagTerm.compareTo(indexTerms[mid]);
        if (delta < 0)
          hi = mid - 1;
        else if (delta > 0)
          lo = mid + 1;
        else
          return mid;
      }
      return hi;
    }

    final void scanIndex(TagTerm tagTerm) throws IOException {
      int offset = getIndexOffset(tagTerm);
      seek(indexPointers[offset], indexTerms[offset]);
    }

    final void seek(long pointer, TagTerm tagTerm) throws IOException {
      input.seek(pointer);
      index = tagTerm.index;
      termBuffer.set(tagTerm);
      prevBuffer.reset();
    }

    final int scanTo(TagTerm tagTerm) throws IOException {
      scanBuffer.set(tagTerm);
      int count = 0;
      while (scanBuffer.compareTo(termBuffer) > 0 && next())
        count++;
      return count;
    }

    public Term term() {
      return new Term();
    }

    public TagTerm tagTerm() {
      return new TagTerm(tagTerm.fieldNumber, termBuffer.getText(), tagTerm.index);
    }
    
    public final boolean next() throws IOException {
      index++;
      if (index >= size - 1) {
        prevBuffer.set(termBuffer);
        termBuffer.reset();
        return false;
      }
      prevBuffer.set(termBuffer);
      tagTerm.index = input.readVInt();
      tagTerm.fieldNumber = input.readVInt();
      termBuffer.read(input);
      tagTermInfo.docFreq = input.readVInt();
      byte type = input.readByte();
      if (type == 0) {
        tagTermInfo.docsLength = input.readVInt();
        tagTermInfo.docsPointer = input.getFilePointer();
        input.seek(tagTermInfo.docsPointer + tagTermInfo.docsLength); // seek to next term
      } else {
        tagTermInfo.postingsPointer = input.readVLong();
        tagTermInfo.skipOffset = input.readVInt();
      }
      return true;
    }
  }
  
  public class TermsWriter {
    int index = 0;
    IndexOutput output;
    int interval;
    TermIndexWriter termIndexWriter;
    int lastIndex = 0;

    long write(TagTerm term, int docFreq, byte[] postings, long blocksPointer) throws IOException {
      if (index % interval == 0) {
        termIndexWriter.write(term, output.getFilePointer(), index);
      }
      long pointer = output.getFilePointer();
      output.writeVInt(index - lastIndex);
      output.writeVInt(term.fieldNumber);
      output.writeString(term.text);
      output.writeVInt(docFreq);
      if (postings != null) {
        output.writeByte((byte)0);
        output.writeVInt(postings.length);
        output.writeBytes(postings, 0, postings.length);
      } else {
        output.writeByte((byte)1);
        output.writeVLong(blocksPointer);
      }
      lastIndex = index;
      index++;
      return pointer;
    }
  }

}
